package club.janna.jrpc.rpc.protocol.jrpc;

import club.janna.jrpc.annotation.SPIService;
import club.janna.jrpc.remoting.Channel;
import club.janna.jrpc.remoting.ClientHandler;
import club.janna.jrpc.remoting.Transporter;
import club.janna.jrpc.rpc.Invoker;
import club.janna.jrpc.rpc.RPCInvocation;
import club.janna.jrpc.rpc.RPCResponse;
import club.janna.jrpc.rpc.protocol.Protocol;
import club.janna.jrpc.serialize.Serialization;
import club.janna.jrpc.util.SPIServiceProvider;
import club.janna.jrpc.util.URIUtil;

import java.net.URI;
import java.util.HashMap;
import java.util.Map;

@SPIService("jrpc")
public class JRPCProtocol implements Protocol {

    private JRPCServerHandler serverHandler;

    private Map<String, JRPCInvoker> invokerMap;

    @Override
    public void publish(Class<?> iClass, URI uri, Invoker invoker) {
        if(serverHandler == null) {
            serverHandler = new JRPCServerHandler();
            Transporter transporter = SPIServiceProvider.getTransporter(URIUtil.getParam(uri, "transport"));
            Serialization serialization = SPIServiceProvider.getSerialization(URIUtil.getParam(uri, "serialization"));
            transporter.start(uri, new JRPCCodec(serialization, RPCInvocation.class), serverHandler);
        }
        // 注册invoker
        serverHandler.register(iClass, invoker);
    }

    @Override
    public synchronized Invoker buildInvoker(URI uri) {
        if(invokerMap == null)
            invokerMap = new HashMap<>();

        // 同一台服务器上的多个服务引用，使用同一个连接
        String key = String.format("%s_%s", uri.getHost(), uri.getPort());
        JRPCInvoker invoker = invokerMap.computeIfAbsent(key, k -> {
            Transporter transport = SPIServiceProvider.getTransporter(URIUtil.getParam(uri, "transport"));
            Serialization serialization = SPIServiceProvider.getSerialization(URIUtil.getParam(uri, "serialization"));
            ClientHandler clientHandler = new JRPCClientHandler();
            Channel channel = transport.connect(uri, new JRPCCodec(serialization, RPCResponse.class), clientHandler);
            return new JRPCInvoker(channel, clientHandler, () -> {
                // 关闭通道
                channel.close();
                // 从map中移除
                invokerMap.remove(key);
            });
        });
        invoker.ref();
        return invoker;
    }
}
